#include "lua_bind.hh"
#include "../../plugin/base/rpc_base.hh"

void LuaBinder::TimerManager::addTimer(TimerID timerID, lua_State* co) {
    timerCoMap_[timerID] = co;
}

lua_State* LuaBinder::TimerManager::removeTimer(TimerID timerID) {
    lua_State* co = nullptr;
    auto it = timerCoMap_.find(timerID);
    if (it != timerCoMap_.end()) {
        co = it->second;
        timerCoMap_.erase(it);
    }
    return co;
}

void LuaBinder::TimerManager::clear() {
    for (auto& it : timerCoMap_) {
        FrameworkRef.cancelTimer(it.first);
    }
    timerCoMap_.clear();
}

LuaBinder::PathFinderManager::PathFinderManager()
    : finderSerial_(1) {
}

std::uint32_t LuaBinder::PathFinderManager::add(SiteType siteType, lua_State* co) {
    pathFinderMap_[finderSerial_] = PathFinder{ co };
    return finderSerial_++;
}

void LuaBinder::PathFinderManager::remove(std::uint32_t serial) {
    auto it = pathFinderMap_.find(serial);
    if (it == pathFinderMap_.end()) {
        return;
    }
    pathFinderMap_.erase(it);
}

LuaBinder::PathFinder* LuaBinder::PathFinderManager::get(std::uint32_t serial) {
    auto it = pathFinderMap_.find(serial);
    if (it == pathFinderMap_.end()) {
        return nullptr;
    }
    return &it->second;
}

void LuaBinder::PathFinderManager::clear() {
    pathFinderMap_.clear();
}

void LuaBinder::CoManager::addBusy(CoInfo* coi) {
    busyMap_[coi->ref()] = coi;
}

void LuaBinder::CoManager::addFree(CoInfo* coi) {
    auto it = busyMap_.find(coi->ref());
    if (it != busyMap_.end()) {
        busyMap_.erase(it);
    }
    freeMap_[coi->ref()] = coi;
}

CoInfo* LuaBinder::CoManager::getFree() {
    if (freeMap_.empty()) {
        addFree(CoInfo::newThread());
    }
    auto it = freeMap_.begin();
    auto coi = it->second;
    lua_settop(*coi, 0);
    freeMap_.erase(it);
    return coi;
}

void LuaBinder::CoManager::remove(CoInfo * coi) {
    CoInfo::deleteThread(coi);
}

void LuaBinder::CoManager::clear() {
    for (auto& it : freeMap_) {
        CoInfo::deleteThread(it.second);
    }
    for (auto& it : busyMap_) {
        CoInfo::deleteThread(it.second);
    }
    freeMap_.clear();
    busyMap_.clear();
}


LuaBinder::EntityFinderManager::EntityFinderManager()
    : finderSerial_(1) {
}

std::uint32_t LuaBinder::EntityFinderManager::add(std::uint32_t entityType, lua_State* co) {
    entityFinderMap_[finderSerial_] = EntityFinder{ co };
    return finderSerial_++;
}

void LuaBinder::EntityFinderManager::remove(std::uint32_t serial) {
    auto it = entityFinderMap_.find(serial);
    if (it == entityFinderMap_.end()) {
        return;
    }
    entityFinderMap_.erase(it);
}

LuaBinder::EntityFinder* LuaBinder::EntityFinderManager::get(std::uint32_t serial) {
    auto it = entityFinderMap_.find(serial);
    if (it == entityFinderMap_.end()) {
        return nullptr;
    }
    return &it->second;
}

void LuaBinder::EntityFinderManager::clear() {
    entityFinderMap_.clear();
}

void LuaBinder::EntityFinderManager::setImmeEntityID(std::uint64_t entityID) {
    lastImmeEntityID_ = entityID;
}

std::uint64_t LuaBinder::EntityFinderManager::getImmeEntityID() {
    return lastImmeEntityID_;
}

void LuaBinder::YieldCallManager::add(CoInfo* coi, google::protobuf::Message* ack, PathID pathID,
    std::uint64_t uuid, std::uint64_t rpcID, PathID fromPathID) {
    auto expiredTimerID = FrameworkRef.startTimerOnce(10000,
        [](TimerHandle, UserData userData) {
        auto L = reinterpret_cast<lua_State*>(userData);
        auto call = LuaRef.getCallManager().get(L);
        if (call) {
            errorlog << "Execute RPC call expired, RPCID:" << call->rpcID << endlog;
            LuaRef.getCallManager().remove(L);
        }
    }, *coi
    );
    yieldCallMap_[*coi] = YieldCall{ ack, coi, pathID, uuid, rpcID, fromPathID, expiredTimerID };
}

LuaBinder::YieldCallManager::YieldCall* LuaBinder::YieldCallManager::get(lua_State* L) {
    auto it = yieldCallMap_.find(L);
    if (it == yieldCallMap_.end()) {
        return nullptr;
    }
    return &it->second;
}

void LuaBinder::YieldCallManager::remove(lua_State* L) {
    auto it = yieldCallMap_.find(L);
    if (it == yieldCallMap_.end()) {
        return;
    }
    RPCRef.deletePBProtoMessage(it->second.ack);
    yieldCallMap_.erase(it);
}

void LuaBinder::YieldCallManager::clear() {
    for (auto& it : yieldCallMap_) {
        delete it.second.ack;
    }
    yieldCallMap_.clear();
}

void LuaBinder::UserCoManager::add(CoInfo * coi) {
    threadMap_[coi->ref()] = coi;
    stateMap_[*coi] = coi;
}

void LuaBinder::UserCoManager::remove(CoInfo * coi) {
    threadMap_.erase(coi->ref());
    stateMap_.erase(*coi);
}

CoInfo * LuaBinder::UserCoManager::getCoByRef(int ref, lua_State * L) {
    auto it = threadMap_.find(ref);
    if (it == threadMap_.end()) {
        return nullptr;
    }
    return it->second;
}

CoInfo * LuaBinder::UserCoManager::getCoByL(lua_State * L) {
    auto it = stateMap_.find(L);
    if (it == stateMap_.end()) {
        return nullptr;
    }
    return it->second;
}

lua_State * LuaBinder::UserCoManager::getResumer(lua_State * L) {
    auto it = stateMap_.find(L);
    if (it == stateMap_.end()) {
        return nullptr;
    }
    return it->second->resumer();
}

void LuaBinder::UserCoManager::clear() {
    for (auto it : threadMap_) {
        CoInfo::deleteThread(it.second);
    }
    threadMap_.clear();
    stateMap_.clear();
}
